package com.weilele.base.library.net

import android.widget.Toast
import androidx.lifecycle.LiveData
import com.weilele.mvvm.base.helper.*
import com.weilele.mvvm.gson
import com.weilele.mvvm.okHttpClient
import com.weilele.mvvm.utils.net.createFormBody
import com.weilele.mvvm.utils.net.createJsonBody
import com.weilele.mvvm.utils.activity.toast
import kotlinx.coroutines.suspendCancellableCoroutine
import okhttp3.Call
import okhttp3.Request
import okhttp3.Response
import java.io.IOException
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

private val API_BASE_URL: String
    get() = CommonNet.BASE_URL

//创建一个全局空的map复用
private val emptyParamsMap = mutableMapOf<String, String>()

/**
 * 发送网络请求
 * 以表单格式发送
 */
fun String.sendHttpByFormBody(
        paramsMap: MutableMap<String, String>?,
        isGet: Boolean = false,
        isNeedBaseUrl: Boolean = true
): okhttp3.Call {
    val params = paramsMap ?: emptyParamsMap
    val request = Request.Builder()
    var url = if (isNeedBaseUrl) {
        API_BASE_URL.trimEnd('/') + "/" + this.trimStart('/')
    } else {
        this
    }
    if (isGet) {
        if (params.isNotEmpty()) {
            if (!url.contains('?')) {
                url += "?"
                for ((key, value) in params) {
                    url += "${key}=${value}&"
                }
            }
        }
    } else {
        request.post(params.createFormBody())
    }
    request.url(url.trimEnd('&'))
    return okHttpClient.newCall(request.build())
}

suspend inline fun <reified T> String.sendHttpByFormBody(
        paramsMap: MutableMap<String, String>?,
        isGet: Boolean = false,
        isNeedBaseUrl: Boolean = true
): T {
    return sendHttpByFormBody(paramsMap, isGet, isNeedBaseUrl).awaitCall()
}

/**
 * 发送网络请求
 * 以json格式发送
 * params如果是null，则以get请求发送
 */
fun String.sendHttpByJsonBody(
        params: Any?,
        isNeedBaseUrl: Boolean = true
): Call {
    val request = Request.Builder()
    val url = if (isNeedBaseUrl) {
        API_BASE_URL.trimEnd('/') + "/" + this.trimStart('/')
    } else {
        this
    }
    request.url(url.trimEnd('&'))
    if (params != null) {
        request.post(params.createJsonBody())
    }
    return okHttpClient.newCall(request.build())
}

suspend inline fun <reified T> String.sendHttpByJsonBody(
        params: Any?,
        isNeedBaseUrl: Boolean = true): T {
    return sendHttpByJsonBody(params, isNeedBaseUrl).awaitCall()
}

/**
 * 会抛出异常
 */
suspend inline fun <reified T : Any> Call.awaitCall(): T {
    return suspendCancellableCoroutine { continuation ->
        continuation.invokeOnCancellation {
            cancel()
        }
        enqueue(object : OkHttpCall<T>(T::class.java) {
            override fun onSuccess(result: T) {
                continuation.resume(result)
            }

            override fun onError(t: Throwable, errorCode: Int?) {
                continuation.resumeWithException(t)
            }
        })
    }
}

/**
 * 请求数据没有成功
 */
class NoSuccessException(msg: String?) : IOException(msg)

/**
 * 提供一个快速创建SimpleLiveDataCall的方法
 */
inline fun <reified P> LiveData<StatusValue<P>>.createOkHttpCall(
        isToastError: Boolean = true/*是否toast出错误*/,
        isKeepLastValue: Boolean = false/*是否保留之前已经赋值的数据*/,
        noinline resultCall: Function1<P, Unit>? = null/*结果回调，如果不为null，则LiveBean不走SUCCESS状态，需要自己手动处理*/
): okhttp3.Callback {
    this.running(isKeepLastValue)
    return object : OkHttpCall<P>(P::class.java) {
        override fun onError(t: Throwable, errorCode: Int?) {
            t.printStackTrace()
            this@createOkHttpCall.error(t.message
                    ?: "createOkHttpCall Error", isKeepLastValue, null, errorCode).apply {
                if (isToastError) {
                    this.nonNullMsg.toast(Toast.LENGTH_LONG)
                }
            }
        }

        override fun onSuccess(result: P) {
            if (resultCall != null) {
                resultCall.invoke(result)
            } else {
                this@createOkHttpCall.success(result)
            }
        }
    }
}

/**
 * OkHttp请求回调
 * 这个封装只支持后台返回json格式字符串
 */
abstract class OkHttpCall<T>(private val cls: Class<T>) :
        okhttp3.Callback {
    override fun onFailure(call: Call, e: IOException) {
        onError(e, null)
    }

    override fun onResponse(call: Call, response: Response) {
        if (response.code != 200) {
            onError(
                    NoSuccessException("httpError:code:${response.code},message:${response.message}"),
                    response.code
            )
            return
        }
        val result = response.body
        try {
            val bodyStr = result?.string()
            if (bodyStr.isNullOrBlank()) {
                onError(IOException("HTTP Body Is Null:${response.message}"), null)
            } else {
                //简单判断是否是json字符串
                if (bodyStr.startsWith('{') || bodyStr.startsWith('[')) {
                    val bo = gson.fromJson<T>(bodyStr, cls)
                    onSuccess(bo)
                } else {
                    onError(IOException("HTTP Body Is Not Json:${bodyStr}"), null)
                }
            }
        } catch (e: Throwable) {
            e.printStackTrace()
            onError(IOException(e.message), null)
        }
    }

    //请求成功
    abstract fun onSuccess(result: T)

    //请求失败
    abstract fun onError(t: Throwable, errorCode: Int?)

}
